#pragma once

#include <unistd.h>
#include <sys/types.h>
#include <wait.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>

#include "Util.hpp"


#define WEBROOT "./WebRoot"                 //Web根目录
#define HOMEPAGE "index-backup.html"        //首页
#define ERRORPAGE "myerror.html"            //错误页 404 NotFound
#define HTTPVERSION "HTTP/1.0"              //HTTP版本

#define ENDLINE "\r\n"     
#define SEP ": "

enum StatusCode //状态码
{
    OK = 200,
    BAD_REQUEST = 400,
    NOT_FOUND = 404,
    SERVER_ERROR = 500,
};

//状态码转化为状态码描述语句 如：200 -> OK
static std::string CodeTransform(int statusCode)  
{
    std::string desrib;
    switch (statusCode)
    {
    case 200:
        desrib = "OK";
        break;
    case 404:
        desrib = "Not Found";
        break;
    default:
        desrib = "";
        break;
    }

    return desrib;
}
// class Status2Code
// {
// private:
//     std::unordered_map<int, std::string> Status2CodeMap;
//    
//     void Init()
//     {
//     }
//     Status2Code()
//     {}
//     ~Status2Code()
//     {}
//
// };

//将后缀suffix转化为Content-Type  如  .html --> text/html
static std::string SuffixTransformType(std::string suffix) 
{
    static std::unordered_map<std::string, std::string> suffixMap = 
    {
        {".html","text/html"}, 
        {".css", "text/css"}, 
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"}, 
        {".xml", "application/xml"}
        //....
    };

    auto itr = suffixMap.find(suffix);
    if(itr != suffixMap.end())
    {
        return itr->second;
    }

   return "text/html";

}

class HttpRequest
{
public:
    //Http请求格式
    std::string _requestLine;            //请求行
    std::vector<std::string> _header;    //请求报头
    std::string _blank = ENDLINE;        //空行
    std::string _body;                   //正文

    size_t _contentLength = 0;           //读取的正文长度(Content-Length)

    //请求行解析结果
    std::string _method;                 //请求方法
    std::string _uri;   //path?args      //请求资源
    std::string _path;                   //URI包含的路径
    std::string _suffix;                 //资源后缀(Content-Type)
    std::string _query;                  //URI?后的参数
    std::string _version;                //Http版本号

    //解析请求报头中的各个KV键值对
    std::unordered_map<std::string, std::string> _headerMap;

    //是否需要使用CGI数据处理
    bool _cgi = false;
};

class HttpResponse
{
public:
    //Http响应格式
    std::string _statusLine;              //响应状态行
    std::vector<std::string> _header;     //响应报文
    std::string _blank = ENDLINE;         //空行
    std::string _body;                    //响应正文
    size_t _contentLength = 0;            //正文长度

    //TODO..
    //std::string _path = WEBROOT+=HOEPAGE;                  //返回的实际文件路径
    //状态码
    int _statusCode = OK;
    //path目标文件
    int _fd = -1;
    //目标文件大小
    //size_t _size = 0;                     //发送资源的大小(正文大小)Content-Length
};

//读取分析请求与构建响应
class EndPiont
{
private:
    int _sock;
    HttpRequest _httpRequest;
    HttpResponse _httpResponse ;
    bool _stop = false;

private:
    // 读取请求行
    bool RecvHttpRequestLine()
    {
        if(Util::ReadLine(_sock, _httpRequest._requestLine) > 0)
        {
            _httpRequest._requestLine.resize(_httpRequest._requestLine.size()-1);   // 去掉\n 方便看log
            LOG(NORMAL, _httpRequest._requestLine);
        }
        else
        {
            _stop = true;
        }
        return _stop;
    }

    // 读取请求报头
    bool RecvHttpHeader()
    {
        std::string line;

        while(1)
        {
            line.clear();       //ReadLine 是一个字符一个字符加进line的, 避免重复
            if((Util::ReadLine(_sock, line)) <= 0) 
            {
                _stop = true;
                break;
            } 
            if(line == "\n") 
            {
                LOG(NORMAL,"RecvHttpHeader stop");
                _httpRequest._blank = line;
                break;
            }
            line.resize(line.size()-1);    //去掉换行符
            _httpRequest._header.push_back(line);
            LOG(NORMAL, line);
        }
        return _stop;
    }

        //解析请求行
    void ParseHttpRequestLine()
    {
        //请求行：方法  URI  Http版本号
        auto& line = _httpRequest._requestLine;
        std::stringstream ss(line);
        ss >> _httpRequest._method >>_httpRequest._uri>>_httpRequest._version;
        //将method 转化为全大写
        std::transform(_httpRequest._method.begin(), _httpRequest._method.end(), \
                        _httpRequest._method.begin(), ::toupper);
        // LOG(NORMAL, _httpRequest._method);
        // LOG(NORMAL, _httpRequest._uri);
        // LOG(NORMAL, _httpRequest._version);
    }
    
    //解析请求报头
    void ParseHttpRequestHeader()
    {   
        std::string key;
        std::string value;

        for(auto& e:_httpRequest._header)
        {
            if(Util::CutString(e, SEP, key, value))
            {
                _httpRequest._headerMap[key] = value;

                LOG(NORMAL, key);
                LOG(NORMAL, _httpRequest._headerMap[key]);
            }
        }
    }
 
    
    //判断是否需要读取正文
    bool IsNeedRecvHttpBody()
    {
        auto& method = _httpRequest._method;

        if(method == "POST")    //默认Post才有正文
        {
            auto& headerMap = _httpRequest._headerMap;
            auto iter = headerMap.find("Content-Length");     //从报头的键值对Map中找到Content-Length
            if(iter != headerMap.end())
            {
                LOG(NORMAL, "POST,Content-Length::" + iter->second);
                _httpRequest._contentLength = stoi(iter->second);
                return true;
            }
        }
        return false;
    }
    
    //读取正文
    bool RecvHttpBody()
    {
        if(IsNeedRecvHttpBody())
        {
            int contentLength = _httpRequest._contentLength;
            std::cout << contentLength << std::endl;
            //读取正文到_body
            auto& body =  _httpRequest._body;
            char ch = 0;
            while(contentLength)
            {
                ssize_t s = recv(_sock, &ch, 1, 0);
                if(s > 0)
                {
                    body += ch;
                    contentLength--;
                }
                else
                {
                    _stop = true;
                    break; 
                }
            }
            LOG(NORMAL, body);
        }
        return _stop;
    }
    

    //非CGI  只管打开对应文件描述符
    int ProcessNonCGI()
    {   
        //打开path目标资源文件fd
        std::cout <<"dddduuubbbggg"<< _httpRequest._path.c_str() << std::endl;

        _httpResponse._fd = open(_httpRequest._path.c_str(), O_RDONLY);
        if(_httpResponse._fd >= 0)  //打开目标资源文件成功才能正确返回资源
        {
            return OK;
        }
        LOG(ERROR, "open fd fail");
        return 404;
    }
    
    //CGI处理数据, 返回状态码
    int ProcessCGI()
    {
        int code = OK;                          //返回的状态码
        //数据
        auto& dataBody = _httpRequest._body;    //POST 正文数据体
        auto& bin = _httpRequest._path;         //可执行程序路径

        std::string queryENV;                   //GET参数 环境变量
        std::string methodENV;                  //请求方法 环境变量
        std::string lengthENV;                  //POST正文长度 环境变量
        std::cout << "debug: CGI Model" << std::endl;
        //管道通信
        int input[2];       //子给父
        int output[2];      //父给子
        //创建管道文件
        if(pipe(input)<0)
        {
            LOG(ERROR, "pipe fail");
            return SERVER_ERROR;
        }  
        if(pipe(output)<0)
        {
            LOG(ERROR, "pipe fail");
            return SERVER_ERROR;
        }
        //创建管道文件完毕

        
        //子进程执行另一可执行程序
        pid_t id = fork();
        if(id > 0)
        {
            //父进程...
            close(input[1]);
            close(output[0]);
            //管道通信
            if(_httpRequest._method == "POST")
            {
                size_t size = 0;        //每次实际读取到的字符数
                size_t total = 0;       //总共读取到的字符数
                const char *start = dataBody.c_str();

                std::cout << "debugggg" << dataBody.c_str() << std::endl;
                
                while((total < _httpRequest._contentLength) 
                    && ((size = write(output[1], start + total, dataBody.size() - total)) > 0))
                {
                    total += size;
                }
            }

            //获取数据处理结果
            char ch = 0;
            while(read(input[0], &ch, 1) > 0)
            {
                _httpResponse._body.push_back(ch);
            }
            //LOG(NORMAL, _httpResponse._body);


            int status = 0;      
            pid_t ret = waitpid(id, &status, 0);    //等待子进程退出并获取退出码
            if(ret == id)
            {
                if ( !(WIFEXITED(status)) )             //没退出
                {
                    code = SERVER_ERROR;
                }
                else if ((WEXITSTATUS(status) != 0))    //不正常退出
                {
                    code = BAD_REQUEST;
                }
                else //else if ((WEXITSTATUS(status) == 0))    //正常退出
                {
                    code = OK;
                }
            }
            
            close(input[0]);
            close(output[1]);
        }
        else if(id == 0)
        {
            //子进程...
            close(input[0]);
            close(output[1]);
            //为了让程序替换后失去读写管道文件描述符后，能找到对应的读写管道文件，事先进行重定向
            //程序替换只替换代码和数据，并不替换内核进程相关的数据结构,包括文件描述符数组
            

            //让子进程知道请求方法才能知道数据在环境变量里还是管道文件里
            methodENV = "METHOD=";
            methodENV += _httpRequest._method;
            putenv((char*)methodENV.c_str());
            LOG(NORMAL, "Add methodENV");
            //std::cerr << "ahead:" << methodENV << std::endl;
            //std::cerr << "ahead:" << _httpRequest._path << std::endl;
            if(_httpRequest._method == "GET")
            {
                //GET的参数一般都不大，采用导入环境变量的方法更高效
                queryENV = "QUERY=";
                queryENV += _httpRequest._query;
                putenv((char*)queryENV.c_str());
                LOG(NORMAL, "Add queryENV");
            }
            else if(_httpRequest._method == "POST")
            {
                lengthENV = "CONTENT_LENGTH=";
                lengthENV += std::to_string(_httpRequest._contentLength);
                putenv((char*)lengthENV.c_str());
                LOG(NORMAL, "Add lengthENV");
            }


            dup2(input[1], 1);      //写出
            dup2(output[0], 0);     //读入
            execl(bin.c_str(), bin.c_str(), nullptr);   //程序替换

            std::cerr<<"execl fail" << std::endl;
            exit(-1);
        }
        else
        {
            LOG(ERROR, "fork fail");
            code = 404;
        }

        return code;
    }

public:

    EndPiont(int sock):_sock(sock)
    {}

    //判断是否该stop(出错)
    bool IsStop()
    {
        return _stop;
    }
    
    //接收读取请求
    void RecvHttpRequest()
    {
        if ( (!RecvHttpRequestLine()) && (!RecvHttpHeader()) )//都不能stop(都没出错)
        {
            ParseHttpRequestLine();
            ParseHttpRequestHeader();
            RecvHttpBody();
        }
    }

    //
    void BuildOKResponse()
    {
        std::string line = "Content-Type: ";
        line += SuffixTransformType(_httpRequest._suffix);
        line += ENDLINE;
        _httpResponse._header.push_back(line);

        line = "Content-Length: ";
        if(_httpRequest._cgi)
        {
            line += std::to_string(_httpResponse._body.size());     //若是cgi数据在body里面
        }
        else
        {
            line += std::to_string(_httpResponse._contentLength);   //非cgi则是资源文件大小
        }
        line += ENDLINE;
        _httpResponse._header.push_back(line);

    }

    //处理状态码 404
    void HandlerError() 
    {
        // _httpRequest._path.clear();
        // _httpRequest._path += WEBROOT;
        // _httpRequest._path += "/";
        // _httpRequest._path += ERRORPAGE;
        // _httpResponse._statusCode = NOT_FOUND;
        _httpRequest._cgi = false;
        _httpResponse._fd = open(_httpRequest._path.c_str(), O_RDONLY);  //
        
        if(_httpResponse._fd > 0)
        {
            struct stat st;
            stat(_httpRequest._path.c_str(), &st);
            std::string line = "Content-Type: text/html";
            line += ENDLINE;
            _httpResponse._header.push_back(line);

            _httpResponse._contentLength = st.st_size;
            
            line = "Conten t-Length: ";
            line += std::to_string(st.st_size);
            line += ENDLINE;
            _httpResponse._header.push_back(line);

        }
        else
        {
            LOG(ERROR, "fd open error" );
        }
    }
    //辅助构建 响应状态行以及处理code
    void BuildHttpResponseHelper()
    {
        //构建状态行
        auto& code = _httpResponse._statusCode;
        auto& statusLine = _httpResponse._statusLine;
        statusLine += HTTPVERSION;
        statusLine += " ";
        statusLine += std::to_string(_httpResponse._statusCode);
        statusLine += " ";
        statusLine += CodeTransform(code);
        statusLine += ENDLINE;

        //构建响应报头及正文
        switch(code)
        {
        
        case 200:
            BuildOKResponse();
            break;
        case 400:
            //无
            break;
        case 404:
            HandlerError();
            break;
        default:
            BuildOKResponse();
            break;
        }

    }

    //构建响应
    void BuildHttpResponse()
    {
        struct stat st;     //文件属性
        std::string path;
        std::size_t found = 0;      //查找.后缀 的位置
        auto& code = _httpResponse._statusCode;

        if(_httpRequest._method != "GET" && _httpRequest._method != "POST")     //暂时只处理这两个请求
        {
            LOG(WORRING, "HttpRequest Method Error");
            code = BAD_REQUEST;
            goto END;
        }
        //处理正确请求
        if(_httpRequest._method == "GET")           //处理GET请求..判断GET有没有带参：GET  URI?wd=100&x=1  HTTP/1.1 
        {
            if(!Util::CutString(_httpRequest._uri, "?", _httpRequest._path, _httpRequest._query))  //URI是否带参数
            {
                _httpRequest._path = _httpRequest._uri;             //不带参
            }
            else    //带参-->使用CGI处理参数(数据)
            {
                _httpRequest._cgi = true;
            }
        }
        else if(_httpRequest._method == "POST")     //处理POST请求
        {
            _httpRequest._path = _httpRequest._uri;
            _httpRequest._cgi = true; //需要使用cgi方式来处理POST的数据
        }
        else
        {
            //..
        }
        //开始构建path--起始地址WEBROOT(只能访问这个地址下的资源)
        path += WEBROOT;
        path += _httpRequest._path;
        _httpRequest._path = path;

        if(_httpRequest._path[_httpRequest._path.size()-1] == '/')  //访问根目录，返回首页
        {
            _httpRequest._path += HOMEPAGE;
        }
        
        //使用系统调用stat() 查看文件属性，文件不存在则查看失败返回-1，成功则返回0
        if(stat(_httpRequest._path.c_str(), &st) == 0)//资源路径存在,但不一定能访问：1、目录， 2、可执行程序
        {
            if(S_ISDIR(st.st_mode))    //判断是目录，则访问该目录下的默认首页
            {
                _httpRequest._path += "/";
                _httpRequest._path += HOMEPAGE;
            }                       
            else if((st.st_mode & S_IXUSR)||(st.st_mode & S_IXGRP)||(st.st_mode & S_IXOTH)) //可执行程序：则CGI
            {                                           //S_IX USR/GRP/OTH  拥有者/所属组/其他 的X权限
                _httpRequest._cgi = true;
            }
        }
        else    //资源不存在，错误路径，返回错误页,设置状态码404
        {
            _httpRequest._path.clear();
            _httpRequest._path += WEBROOT;
            _httpRequest._path += "/";
            _httpRequest._path += ERRORPAGE;
            code = 404;

            goto END;
        }
        //最终的path构建完成
        LOG(NORMAL, _httpRequest._path);

        stat(_httpRequest._path.c_str(), &st);  //stat查看文件属性 得到 最终返回资源的大小 size
        _httpResponse._contentLength = st.st_size;
        

        //分析资源类型--找后缀
        found = _httpRequest._path.rfind(".");
        if(found == std::string::npos)  //没有后缀
        {
            _httpRequest._suffix = ".html"; 
        }
        else
        {
            _httpRequest._suffix =  _httpRequest._path.substr(found);
        }
        
        //判断是否需要CGI处理
        if(_httpRequest._cgi)
        {
            code = ProcessCGI();         //执行目标程序， 并返回处理结果到响应报文 Respose._body
        }
        else
        {
            code = ProcessNonCGI();      //只需要打开资源文件描述符即可
        }
END:    //END 准备返回  
        //最后处理错误码问题
        //构建响应
        BuildHttpResponseHelper();
    return;
    }

    //发送响应
    void SendHttpBack()
    {
        //拷贝状态行
        send(_sock, _httpResponse._statusLine.c_str(), _httpResponse._statusLine.size(), 0);
        //拷贝报头
        for(auto& e : _httpResponse._header)
        {
            send(_sock, e.c_str(),e.size(), 0);
        }
        //空行
        //send(_sock, ENDLINE, sizeof(ENDLINE)-1, 0);
        send(_sock, _httpResponse._blank.c_str(), _httpResponse._blank.size(), 0);

        //资源: fd  or  _httpResponse._body
        if(_httpRequest._cgi)
        {
            //cgi处理的结果在_httpResponse._body内，且并非文件不能用senfile()
            //sendfile(_sock, _httpResponse._fd, nullptr,  _httpResponse._body.size());//sendfile最后一个参数 文件大小
            size_t total = 0;
            size_t size = 0;
            const char* start = _httpResponse._body.c_str();
            while((total < _httpResponse._body.size()) 
                && (size = send( _sock, start + total, _httpResponse._body.size() - total, 0)) > 0)
            {
                total += size;
            }

        }
        else
        {
            sendfile(_sock, _httpResponse._fd, nullptr,  _httpResponse._contentLength); //sendfile最后一个参数 文件大小
        }

        //sendfile(_sock, _httpResponse._fd, nullptr,  st.st_size);
        close(_httpResponse._fd);
    }

    ~EndPiont()
    {
        close(_sock);
    }
};

//让线程执行的处理方法入口
class CallBack
{
public:

    CallBack()
    {
    }
    void operator()(int sock)
    {
        RequestHandler(sock);
    }
    
    //让线程执行的处理方法
    void RequestHandler(int sock)
    {
        //(NORMAL, "Request handling..");

#ifdef DEBUG
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer), 0);
        std::cout << "------------------------------" << std::endl;
        std::cout << buffer << std::endl;
        std::cout << "------------------------------" << std::endl;
#else
        EndPiont* ep = new EndPiont(sock);
        ep->RecvHttpRequest();
        if(ep->IsStop())
        {
            LOG(NORMAL, "Recv Failed , Stopped!");
        }
        else
        { 
            ep->BuildHttpResponse();
            ep->SendHttpBack();
        }
        delete ep;
#endif

        LOG(NORMAL, "Hand requset over");
    }

    ~CallBack()
    {
    }
};


//  end  //